%{ open Ast %}

%token <string> NAME
%token <string> DATATYPE
%token <int> INT
%token <string> STRING
%token <string> BOOL

%token LPAREN RPAREN LBRACE RBRACE
%token SEMICOLON COMMA
%token ASSIGN PLUS MINUS TIMES DIVIDE
%token GT GTEQ LT LTEQ EQ NEQ AND OR NOT
%token IF ELSE FOR WHILE BREAK CONTINUE RETURN
%token EOL EOF

%right ASSIGN
%left AND OR
%left GT GTEQ LT LTEQ EQ NEQ
%right NOT
%left PLUS MINUS
%left TIMES DIVIDE

%start parse
%type <Ast.parse> parse

%%

parse:
			{ [], [] }
	| parse vdecl	{ ($2 :: fst $1), snd $1 }
	| parse fdecl	{ fst $1, ($2 :: snd $1) }

fdecl:
DATATYPE NAME LPAREN parameters RPAREN LBRACE vdecls operations RBRACE
{{
	rettype = $1;
	fname = $2;
	parameters = $4;
	fields = List.rev $7;
	fbody = List.rev $8;
}}

parameters:
					{ [] }
	| par_list			{ List.rev $1 }
par_list:
	  DATATYPE NAME			{ [($1, $2)] }
	| par_list COMMA DATATYPE NAME	{ ($3, $4) :: $1 }

vdecls:
					{ [] }
	| vdecls vdecl			{ $2 :: $1 }
vdecl:
	DATATYPE NAME SEMICOLON		{ ($1, $2) }

operations:
					{ [] }
	| operations operation		{ $2 :: $1 }
operation:
	  expr SEMICOLON		{ Expr($1) }
	| LBRACE operations RBRACE	{ Block(List.rev $2) }
	| IF LPAREN expr RPAREN operation ELSE operation
					{ Condition($3, $5, $7) }
	| WHILE LPAREN expr RPAREN operation
					{ While($3, $5) }
	| BREAK SEMICOLON		{ Break }
	| CONTINUE SEMICOLON		{ Continue }
	| RETURN expr SEMICOLON		{ Return($2) }

expr:
/* Datatypes: */
	  INT			{ Int($1) }
	| STRING		{ String($1) }
	| BOOL			{ Bool($1) }
	| NAME			{ Name($1) }
/* Ops */
	| expr PLUS expr	{ Binop($1, Plus, $3) }
	| expr MINUS expr	{ Binop($1, Minus, $3) }
	| expr TIMES expr	{ Binop($1, Times, $3) }
	| expr DIVIDE expr	{ Binop($1, Divide, $3) }
/* Assignment */
	| NAME ASSIGN expr	{ Assign($1, $3) }
/* Comparison */
	| expr GT expr		{ Binop($1, Gt, $3) }
	| expr GTEQ expr	{ Binop($1, Gteq, $3) }
	| expr LT expr		{ Binop($1, Lt, $3) }
	| expr LTEQ expr	{ Binop($1, Lteq, $3) }
	| expr EQ expr		{ Binop($1, Eq, $3) }
	| expr NEQ expr		{ Binop($1, Neq, $3) }
/* Logical */
	| NOT expr		{ Not($2) }
	| expr AND expr		{ Binop($1, And, $3) }
	| expr OR expr		{ Binop($1, Or, $3) }

